package mars.ns
import mars.ns.core._
import mars.ns.listeners._
import java.lang.Thread
import mars.ns.core.Network
import mars.ns.listeners.EventExecutionListener
import mars.ns.selectors.NodeSelection

object Simulation{
	/**
     * @return a function that checks if the given number is still smaller than the max iterations count
     */
    implicit def toMaxIterationFunction(amount : Int) : Int => Boolean = {
		return ((iteration : Int) =>  (iteration > amount ))
    }
    def allConnectedFunction[T <: Node](network : Network[T]) : Int => Boolean = {
      return ((_ : Int) => network.getComponents().size ==1)
    }
}

abstract class Simulation[T<: Node](var network: Network[T],val isEnded: Int => Boolean) extends
	NodeSelection[T] {
	// safety builtin : Maximum 100000 iterations, to avoid infinite isEnded functions
    final val MAX_ITERATIONS : Int = 100000
    
    var listeners:List[SimulationListener[T]] = Nil
	private val eventExecuter : EventExecutionListener[T] = new EventExecutionListener[T](this)

    /**
     * Constructor giving a network and an amount of iterations that the simulation may run
     */
    def this(network : Network[T],iterations : Int) =
      this(network, Simulation.toMaxIterationFunction(iterations) )

    def run() {
        subscribe(eventExecuter)
      	var i = 1;
        while(i <= MAX_ITERATIONS && !(isEnded(i))) {
           // println("Iteration: "+i)
            nextIteration
        	notifyNewIteration(i)
        	i += 1
        }
    }
    /**
     * Add an event to the eventExecuter
     */
    def addEvent(node : T, event : (T=>Unit)) {eventExecuter.addEvent(node,event)}
    
    def subscribe(listener : SimulationListener[T] ){
        listeners = listener::listeners
    }
    def notifyNewIteration(i:Int) = {
        listeners.foreach(arg => arg.update(network,i))
    }
    def notifyLinkAdded(from : T, to:T, lType :LinkType){
        listeners.foreach(arg => arg.edgeAdded(from, to, lType))
    }
    def notifyNodeChanged(node :T){
        listeners.foreach(arg => arg.nodeChanged(node))
    }

    
    def nextIteration    
}